package org.example.http;

import org.example.http.demo.*;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class TomcatImpl implements Tomcat {

    private int port;
    private String webRoot;
    // String => servlet 地址
    // Object => servlet 类对象 或 servlet 对象
    private Map<String,Object> servletMap = new HashMap<>();

    @Override
    public void start(int port, String webRoot) throws IOException, InterruptedException {
        this.port = port;
        this.webRoot = webRoot;
        ServerSocket server = new ServerSocket(port);
        System.out.println("Tomcat started on port " + port);

        // 注册 Servlet
        registerServlet();

        // 监听停止服务指令
        new Thread(()->{
            try {
                ServerSocket shutdownServer = new ServerSocket(1099);
                System.out.println("listening shutdown on port 1099");
                shutdownServer.accept();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            listenShutdown();
        }).start();

        Thread t1 = new Thread(()->{
            while (true) {
                /**
                 * 问题: 如果现在监听到shutdown指令, 但是还没有处理完当前的请求, 会发生什么?
                 * 答案: 会等待当前的请求处理完成, 然后再停止服务
                 * 问题: 如何接收到shutdown指令后, 立即退出?
                 */
                try {
                    Socket socket = server.accept();
                    new Thread(()->{
                        try {
                            System.out.println("New client connected" + socket.getInetAddress());
                            // 构建请求对象
                            HttpServletRequest request = buildRequest(socket);
                            // 构建响应对象
                            HttpServletResponse response = buildResponse(socket, request);
                            // 判断是否是动态请求
                            if (isServletRequest(request)) {
                                processServletRequest(request, response);
                                       // 判断静态请求
                            } else if (isStaticRequest(request)) {
//                                OutputStream out = socket.getOutputStream();
//                                out.write("HTTP/1.1 200 OK\r\n".getBytes());
//                                out.write("Content-Type: text/html;charset=UTF-8\r\n".getBytes());
//                                out.write("\r\n".getBytes());
//                                out.write("<h1>Hello World!</h1>\r\n".getBytes());
//                                out.flush();
                                processStaticRequest(request, response);
                            } else {
                                // 404 资源不存在
                                // TODO 请添加404页面
                            }
                        } catch (IOException | ServletException e) {
                            throw new RuntimeException(e);
                        } finally {
                            try {
                                socket.close();
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }).start();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        // 守护线程
        t1.setDaemon(true);
        t1.start();

        // 让主线程再此等待
        synchronized (this) {
            this.wait();
        }

        server.close();
        System.out.println("Tomcat stopped");

    }

    @Override
    public HttpServletRequest buildRequest(Socket socket) throws IOException {
        return new HttpServletRequestImpl(socket);
    }

    @Override
    public HttpServletResponse buildResponse(Socket socket, HttpServletRequest request) throws IOException {
        return new HttpServletResponseImpl(socket);
    }

    @Override
    public boolean isStaticRequest(HttpServletRequest request) {
        // 判断访问的资源文件是否存在
        String requestURI = request.getRequestURI();
        String filepath = webRoot + requestURI;
        File file = new File(filepath);
        return file.exists();
    }

    @Override
    public boolean isServletRequest(HttpServletRequest request) {
        // /add => AddServlet   /add?a=1&b=2
        // /add\\??.*
        // 判断servlet地址是否存
        long count = servletMap.keySet().stream()
                .filter(path -> request.getRequestURI().matches(path+"(\\?.*)?"))
                .count();
        return count > 0;
    }

    @Override
    public void processStaticRequest(HttpServletRequest request,
                                     HttpServletResponse response) throws IOException {
        String requestURI = request.getRequestURI();
        String filepath = webRoot + requestURI;
        File file = new File(filepath);
        String contentType = getMimeType(filepath);
        OutputStream out = response.getOutputStream();
        out.write("HTTP/1.1 200 OK\r\n".getBytes());
        out.write(String.format("Content-Type: %s\r\n", contentType).getBytes());
        out.write("\r\n".getBytes());

        try(FileInputStream fos = new FileInputStream(file)){
            int len;
            byte[] bytes = new byte[1024];
            while ((len = fos.read(bytes)) != -1) {
                out.write(bytes, 0, len);
            }
        }
    }

    @Override
    public void processServletRequest(HttpServletRequest request,
                                      HttpServletResponse response) throws IOException, ServletException {
        // 1. 获取请求路径
        String requestURI = request.getRequestURI();
        if(requestURI.contains("?")){
            requestURI = requestURI.substring(0, requestURI.indexOf("?"));
        }
        // 2. 获取servlet对象
        Object servlet = servletMap.get(requestURI);
        // 3. 判断servlet是否是对象, 如果不是对象, 则创建对象
        if (servlet instanceof Class) {
            // 创建servlet对象
            servlet = loadServlet((Class<HttpServlet>) servlet);
            // 替换servlet对象
            servletMap.put(requestURI, servlet);
        }

        // 获取会话ID, 将会话ID添加Cookie中
        HttpSession session = request.getSession();
        String sessionId = session.getId();
        Cookie cookie = new CookieImpl("JSESSIONID", sessionId);
        cookie.setPath("/");
        response.addCookie(cookie);

        // 4. 调用servlet的service方法
        ((HttpServlet)servlet).service(request, response);
        // 5. 刷新缓冲区, 输出响应报文
        response.flushBuffer();
    }

    @Override
    public String getMimeType(String path) {
        if (path.endsWith(".html")) {
            return "text/html;charset=UTF-8";
        } else if (path.endsWith(".css")) {
            return "text/css;charset=UTF-8";
        } else if (path.endsWith(".js")) {
            return "text/javascript;charset=UTF-8";
        } else if (path.endsWith(".jpg")) {
            return "image/jpeg";
        } else if (path.endsWith(".png")) {
            return "image/png";
        } else if (path.endsWith(".gif")) {
            return "image/gif";
        } else if (path.endsWith(".txt")) {
            return "text/plain;charset=UTF-8";
        } else if (path.endsWith(".json")) {
            return "application/json;charset=UTF-8";
        } else {
            return "text/html;charset=UTF-8";
        }
    }

    @Override
    public void listenShutdown() {
        System.out.println("Tomcat shutting down");
        synchronized (this) {
            this.notify();
        }
    }

    // 注册 Servlet
    private void registerServlet(){
        servletMap.put("/hello", HelloServlet.class);
        servletMap.put("/mng.do", ToMangeServlet.class);
        servletMap.put("/getCookie", GetCookieServlet.class);
        servletMap.put("/session", SessionServlet.class);
        servletMap.put("/login", LoginServlet.class);
        servletMap.put("/logout", LogoutServlet.class);
        servletMap.put("/myInfo", MyInfoServlet.class);
        servletMap.forEach((k,v)-> System.out.println(k + " => " + v));
    }

    // 创建 Servlet, 默认访问时创建
    private HttpServlet loadServlet(Class<HttpServlet> servletClass){
        try {
            HttpServlet servlet = servletClass.newInstance();
            servlet.init();
            return servlet;
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        new TomcatImpl().start(8080, "E:\\录屏\\154\\0808");
    }
}
